{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Writing Function" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "-" }, "tags": [ "remove-cell" ] }, "source": [ "**CS1302 Introduction to Computer Programming**\n", "___" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2020-11-27T11:20:04.656873Z", "start_time": "2020-11-27T11:20:04.651575Z" }, "slideshow": { "slide_type": "fragment" }, "tags": [ "remove-cell" ] }, "outputs": [], "source": [ "%reload_ext mytutor" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Function Definition" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "**How to write a function?**" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "A function is defined using the [`def` keyword](https://docs.python.org/3/tutorial/controlflow.html#defining-functions)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "E.g., a simple function that prints \"Hello, World!\" can be defined as follows:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T10:57:20.079405Z", "start_time": "2020-09-26T10:57:20.074270Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# Function definition\n", "def say_hello():\n", " print(\"Hello, World!\")" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T10:57:23.433464Z", "start_time": "2020-09-26T10:57:23.428654Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, World!\n" ] } ], "source": [ "# Function invocation\n", "say_hello()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "To make a function more powerful and solve different problems,\n", "- use a [return statement](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement) to return a value that\n", "- depends on some input arguments." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T10:57:44.785181Z", "start_time": "2020-09-26T10:57:44.764752Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def increment(x):\n", " return x + 1\n", "\n", "\n", "increment(3)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We can also have multiple input arguments." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T10:57:54.035276Z", "start_time": "2020-09-26T10:57:54.029723Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def length_of_hypotenuse(a, b):\n", " if a >= 0 and b >= 0:\n", " return (a ** 2 + b ** 2) ** 0.5\n", " else:\n", " print(\"Input arguments must be non-negative.\")" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T10:58:12.060065Z", "start_time": "2020-09-26T10:58:12.053516Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "5.0" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "length_of_hypotenuse(3, 4)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T10:58:15.167643Z", "start_time": "2020-09-26T10:58:15.162160Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Input arguments must be non-negative.\n" ] } ], "source": [ "length_of_hypotenuse(-3, 4)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Documentation" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**How to document a function?**" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T10:58:32.291732Z", "start_time": "2020-09-26T10:58:32.286171Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# Author: John Doe\n", "# Last modified: 2020-09-14\n", "def increment(x):\n", " \"\"\"Increment by 1.\n", "\n", " A simple demo of\n", " - parameter passing,\n", " - return statement, and\n", " - function documentation.\"\"\"\n", " return x + 1 # + operation is used and may fail for 'str'" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The `help` command shows the docstring we write \n", "- at the beginning of the function body\n", "- delimited using triple single/double quotes. " ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T10:58:37.897312Z", "start_time": "2020-09-26T10:58:37.890013Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function increment in module __main__:\n", "\n", "increment(x)\n", " The function takes in a value x and returns the increment x + 1.\n", " \n", " It is a simple example that demonstrates the idea of\n", " - parameter passing, \n", " - return statement, and \n", " - function documentation.\n", "\n" ] } ], "source": [ "help(increment)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The docstring should contain the *usage guide*, i.e., information for new users to call the function properly. See Python style guide (PEP 257) for\n", "- [one-line docstrings](https://www.python.org/dev/peps/pep-0257/#one-line-docstrings) and\n", "- [multi-line docstrings](https://www.python.org/dev/peps/pep-0257/#multi-line-docstrings)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**Why doesn't `help` show the comments that start with `#`?**" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "```Python\n", "# Author: John Doe\n", "# Last modified: 2020-09-14\n", "def increment(x):\n", " ...\n", " return x + 1 # + operation is used and may fail for 'str'\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Those comments are not usage guide. They are intended for programmers who need to maintain/extend the function definition:" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Information about the author and modification date facilitate communications among programmers.\n", "- Comments within the code help explain important and not-so-obvious implementation details." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**How to let user know the data types of input arguments and return value?**" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We can [annotate](https://docs.python.org/3/library/typing.html) the function with *hints* of the types of the arguments and return value." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T04:21:20.009950Z", "start_time": "2020-09-26T04:21:20.003223Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function increment in module __main__:\n", "\n", "increment(x: float) -> float\n", " The function takes in a value x and returns the increment x + 1.\n", " \n", " It is a simple example that demonstrates the idea of\n", " - parameter passing,\n", " - return statement, and\n", " - function documentation.\n", "\n" ] } ], "source": [ "# Author: John Doe\n", "# Last modified: 2020-09-14\n", "def increment(x: float) -> float:\n", " \"\"\"Increment by 1.\n", "\n", " A simple demo of\n", " - parameter passing,\n", " - return statement, and\n", " - function documentation.\"\"\"\n", " return x + 1 # + operation is used and may fail for 'str'\n", "\n", "\n", "help(increment)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Annotations make the code easier to understand and can be used by editor with type-checking tools. However, annotations are not enforced by the Python interpreter." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T11:01:10.903133Z", "start_time": "2020-09-26T11:01:10.899325Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "def increment_user_input():\n", " return increment(input()) # does not raise error even though input returns str" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T11:01:33.691263Z", "start_time": "2020-09-26T11:01:29.588117Z" }, "slideshow": { "slide_type": "-" }, "tags": [ "remove-output" ] }, "outputs": [ { "ename": "TypeError", "evalue": "can only concatenate str (not \"int\") to str", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/tmp/ipykernel_566/3886321302.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mincrement_user_input\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# still lead to runtime error\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m/tmp/ipykernel_566/2562968551.py\u001b[0m in \u001b[0;36mincrement_user_input\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mincrement_user_input\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mincrement\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minput\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# does not raise error even though input returns str\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m/tmp/ipykernel_566/3956087454.py\u001b[0m in \u001b[0;36mincrement\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mand\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m - function documentation.'''\n\u001b[0;32m---> 10\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;31m# + operation is used and may fail for 'str'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: can only concatenate str (not \"int\") to str" ] } ], "source": [ "increment_user_input() # still lead to runtime error" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "The types can also be described in the docstring following the [Numpy](https://numpydoc.readthedocs.io/en/latest/format.html#parameters) or [Google](https://google.github.io/styleguide/pyguide.html#383-functions-and-methods) style. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Author: John Doe\n", "# Last modified: 2020-09-14\n", "def increment(x: float) -> float:\n", " \"\"\"Increment by 1.\n", "\n", " A simple demo of\n", " - parameter passing,\n", " - return statement, and\n", " - function documentation.\n", "\n", " Parameters\n", " ----------\n", " x: float\n", " Value to be incremented.\n", "\n", " Returns\n", " -------\n", " float:\n", " Value of x incremented by 1.\n", " \"\"\"\n", " return x + 1 # + operation is used and may fail for 'str'\n", "\n", "\n", "help(increment)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" }, "tags": [] }, "source": [ "There are tools such as [sphinx](https://www.sphinx-doc.org/) to generate documentation in html format and compile the [docstrings automatically into an API reference](https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Parameter Passing" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "**Can we increment a variable instead of returning its increment?**" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T11:02:06.564099Z", "start_time": "2020-09-26T11:02:06.559581Z" }, "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "def increment(x):\n", " x += 1" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T11:02:10.712572Z", "start_time": "2020-09-26T11:02:10.708004Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n" ] } ], "source": [ "x = 3\n", "increment(x)\n", "print(x) # 4?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "Does the above code increment `x`?" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "ExecuteTime": { "end_time": "2020-09-26T11:03:15.848468Z", "start_time": "2020-09-26T11:03:15.843151Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%mytutor -h 350\n", "def increment(x):\n", " x += 1\n", "\n", "\n", "x = 3\n", "increment(x)\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Step 3: The function `increment` is invoked with an argument `x`.\n", "- Step 3-4: A local frame is created for variables local to `increment` during its execution. \n", " - The *formal parameter* `x` in `def increment(x):` becomes a local variable and\n", " - it is assigned the value `3` of the *actual parameter* given by the global variable `x`.\n", "- Step 5-6: The local (but not the global) variable `x` is incremented.\n", "- Step 6-7: The function call completes and the local frame is removed." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In other languages such as C++, there is a way to [pass the argument by reference](https://pythontutor.com/cpp.html#code=void%20increment%28int%20%26%20x%29%20%7B%0A%20%20x%20%2B%3D%201%3B%0A%7D%0A%0Aint%20main%28%29%20%7B%0A%20%20int%20x%20%3D%203%3B%0A%20%20increment%28x%29%3B%0A%20%20return%200%3B%0A%7D&mode=edit&origin=opt-frontend.js&py=cpp_g%2B%2B9.3.0&rawInputLstJSON=%5B%5D) so that the local parameter points to the same location as the global parameter." ] } ], "metadata": { "celltoolbar": "Slideshow", "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" }, "latex_envs": { "LaTeX_envs_menu_present": true, "autoclose": false, "autocomplete": true, "bibliofile": "biblio.bib", "cite_by": "apalike", "current_citInitial": 1, "eqLabelWithNumbers": true, "eqNumInitial": 1, "hotkeys": { "equation": "Ctrl-E", "itemize": "Ctrl-I" }, "labels_anchors": false, "latex_user_defs": false, "report_style_numbering": false, "user_envs_cfg": false }, "rise": { "enable_chalkboard": true, "scroll": true, "theme": "white" }, "toc": { "base_numbering": 1, "nav_menu": { "height": "195px", "width": "330px" }, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "454.418px", "left": "1533px", "top": "110.284px", "width": "435.327px" }, "toc_section_display": true, "toc_window_display": false }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }